#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <ctype.h>

#define SIZE 1024
#define MAX_ARGC 64
#define SEP " "
#define STREND '\0'

// 下面的都和重定向有关
#define NoneRedir -1
#define StdinRedir 0
#define StdoutRedir 1
#define AppendRedir 2

#define IgnSpace(buf, pos)        \
    do                            \
    {                             \
        while (isspace(buf[pos])) \
            pos++;                \
    } while (0)

int redir_type = NoneRedir;
char *filename = NULL;

char *argv[MAX_ARGC];
char pwd[SIZE];
char env[SIZE]; // for test
int lastcode = 0;

char *UserName()
{
    char *UserName = getenv("USER");
    if (UserName)
        return UserName;
    else
        return "None";
}

char *HostName()
{
    char *hostname = malloc(256);
    if (gethostname(hostname, 255) != 0)
    {
        free(hostname);
        return NULL;
    }
    return hostname;
}

char *CurrentWorkDir()
{
    char *CurrentWorkDir = getenv("PWD");
    // printf("%s\n",CurrentWorkDir);
    if (CurrentWorkDir)
        return CurrentWorkDir;
    else
        return "None";
}

char *Home()
{
    return getenv("HOME");
}

int Interactive(char *out, int size)
{
    // 输出提示符并获取命令行输入
    char *hostname = HostName();
    printf("%s@%s:%s$ ", UserName(), hostname, CurrentWorkDir());
    free(hostname);

    fgets(out, size, stdin);
    out[strlen(out) - 1] = '\0'; // 去掉换行符

    return strlen(out);
}

void CheckRedir(char in[])
{
    // ls -a -l
    // ls -a -l > log.txt
    // ls -a -l >> log.txt
    // cat < log.txt
    redir_type = NoneRedir;
    filename = NULL;
    int pos = strlen(in) - 1;
    while (pos >= 0)
    {
        if (in[pos] == '>')
        {
            if (in[pos - 1] == '>')
            {
                redir_type = AppendRedir;
                in[pos - 1] = STREND;
                ++pos;
                IgnSpace(in, pos);
                filename = in + pos;
                break;
            }
            else
            {
                redir_type = StdoutRedir;
                in[pos] = STREND;
                ++pos;
                IgnSpace(in, pos);
                filename = in + pos;
                //printf("debug: %s, %d\n", filename, redir_type);
                break;
            }
        }
        else if (in[pos] == '<')
        {
            redir_type = StdinRedir;
            in[pos] = STREND;
            ++pos;
            IgnSpace(in, pos);
            filename = in + pos;
            //printf("debug: %s, %d\n", filename, redir_type);
            break;
        }
        else
            pos--;
    }
}

void Split(char in[])
{
    CheckRedir(in);
    int i = 0;
    argv[i++] = strtok(in, SEP);
    while (argv[i++] = strtok(NULL, SEP));

    if (strcmp(argv[0], "ls") == 0)
    {
        argv[i - 1] = (char *)"--color";
        argv[i] = NULL;
    }

    // printf("argv:");
    // for(int i=0;argv[i];i++)
    //     printf("%s ",argv[i]);
}

void Execute()
{
    pid_t id = fork();
    if (id == 0)
    {
        int fd = -1;
        if (redir_type == StdinRedir)
        {
            fd = open(filename, O_RDONLY);
            dup2(fd, STDIN_FILENO);
        }
        else if (redir_type == StdoutRedir)
        {
            fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0666);
            dup2(fd, STDOUT_FILENO);
            //printf("Execute Success!\n");
        }
        else if (redir_type == AppendRedir)
        {
            fd = open(filename, O_WRONLY | O_CREAT | O_APPEND, 0666);
            dup2(fd, STDOUT_FILENO);
        }

        execvp(argv[0], argv);
        exit(1);
    }

    int status;
    pid_t rid = waitpid(id, &status, 0);
    if (rid == id)
        lastcode = WEXITSTATUS(status);

    // printf("run done,rid:%d\n",rid);
}

int BuildinCmd()
{
    // 检测是否为内建命令
    int flag = 0;

    if (strcmp(argv[0], "cd") == 0)
    {
        flag = 1;

        char *target = argv[1];
        if (!target)
            target = Home();
        chdir(target);
        char temp[64];
        getcwd(temp, 64);
        snprintf(pwd, SIZE, "PWD=%s", temp);
        putenv(pwd);
    }
    else if (strcmp(argv[0], "export") == 0)
    {
        flag = 1;
        if (argv[1])
        {
            strcpy(env, argv[1]);
            putenv(env);
        }
    }
    else if (strcmp(argv[0], "echo") == 0)
    {
        flag = 1;
        if (argv[1] == NULL)
            printf("\n");
        else if (argv[1][0] == '$')
        {
            if (argv[1][1] == '?')
            {
                printf("%d\n", lastcode);
                lastcode = 0;
            }
            else
            {
                char *e = getenv(argv[1] + 1);
                if (e)
                    printf("%s\n", e);
            }
        }
        else
        {
            int i=1;
            while(argv[i])
            {
                printf("%s ", argv[i++]);
            }
            printf("\n");
        }
    }

    return flag;
}

int main()
{
    while (1)
    {
        char conmandline[SIZE];
        // 1. 打印命令行提示符，获取用户输入的命令字符串
        int n = Interactive(conmandline, SIZE);
        if (n == 0)
            continue;

        // 2. 分割命令行
        Split(conmandline);

        // 3. 处理内建命令
        n = BuildinCmd();
        if (n)
            continue;

        // 4. 执行命令
        Execute();
    }

    return 0;
}